package jmine.tec.web.servlet.delegate;

import java.io.IOException;

import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

import org.springframework.beans.factory.BeanCreationException;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

/**
 * Servlet que vai delegar para um bean definido no spring
 * 
 * @author takeshi
 */
public class DelegatingServletProxy implements Servlet {

    private String contextAttribute;

    private String targetBeanName;

    private boolean targetServletLifecycle = false;

    private Servlet delegate;

    private final Object delegateMonitor = new Object[]{};

    private ServletConfig config;

    /**
     * {@inheritDoc}
     */
    public void destroy() {
        synchronized (this.delegateMonitor) {
            if (this.getDelegate() != null) {
                this.getDelegate().destroy();
            }
        }
    }

    /**
     * {@inheritDoc}
     */
    public ServletConfig getServletConfig() {
        return this.config;
    }

    /**
     * {@inheritDoc}
     */
    public String getServletInfo() {
        Servlet servlet = this.getDelegate();
        if (servlet != null) {
            return servlet.getServletInfo();
        }
        return null;
    }

    /**
     * {@inheritDoc}
     */
    public void init(ServletConfig servletConfig) throws ServletException {
        this.config = servletConfig;
        this.targetBeanName = servletConfig.getInitParameter("targetBeanName");
        this.targetServletLifecycle = Boolean.parseBoolean(servletConfig.getInitParameter("targetServletLifecycle"));
    }

    /**
     * {@inheritDoc}
     */
    public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
        this.getDelegate().service(req, res);
    }

    /**
     * @return the targetServletLifecycle
     */
    public boolean isTargetServletLifecycle() {
        return this.targetServletLifecycle;
    }

    /**
     * @return the delegate
     */
    public Servlet getDelegate() {
        synchronized (this.delegateMonitor) {
            if (this.delegate == null) {
                WebApplicationContext ctx = this.getWebApplicationContext();
                if (this.targetBeanName == null) {
                    this.targetBeanName = this.config.getServletName();
                }
                Servlet servlet = (Servlet) ctx.getBean(this.targetBeanName, Servlet.class);
                // this.delegate =
                if (this.targetServletLifecycle) {
                    try {
                        servlet.init(this.config);
                    } catch (ServletException e) {
                        throw new BeanCreationException(e.getMessage(), e);
                    }
                }
                this.delegate = servlet;
            }
        }
        return this.delegate;
    }

    /**
     * Procura um {@link WebApplicationContext}
     * 
     * @return {@link WebApplicationContext}
     */
    private WebApplicationContext getWebApplicationContext() {
        if (this.contextAttribute == null) {
            return WebApplicationContextUtils.getWebApplicationContext(this.config.getServletContext());
        }
        return WebApplicationContextUtils.getWebApplicationContext(this.config.getServletContext(), this.contextAttribute);
    }

    /**
     * @return the config
     */
    public ServletConfig getConfig() {
        return this.config;
    }

    /**
     * @return the contextAttribute
     */
    public String getContextAttribute() {
        return this.contextAttribute;
    }

    /**
     * @param contextAttribute the contextAttribute to set
     */
    public void setContextAttribute(String contextAttribute) {
        this.contextAttribute = contextAttribute;
    }

    /**
     * @return the targetBeanName
     */
    public String getTargetBeanName() {
        return this.targetBeanName;
    }

    /**
     * @param targetBeanName the targetBeanName to set
     */
    public void setTargetBeanName(String targetBeanName) {
        this.targetBeanName = targetBeanName;
    }

}
